package org.goldenport.g3

import scala.xml.Node
import com.google.inject.AbstractModule
import org.goldenport.Goldenport
import org.goldenport.application.GApplicationDescriptor
import org.goldenport.g3.adapters._
import org.goldenport.g3.messages._
import javax.servlet.http.HttpServlet
import org.goldenport.sdoc.SDoc

// GoldenportBus Feb. 20, 2010
// G3Application Mar. 19, 2010
/**
 * 
 * @since   May.  6, 2010
 * @version Apr.  4, 2011
 * @version Dec.  4, 2011
 * @author  ASAMI, Tomoharu
 */
abstract class G3Module extends AbstractModule with G3Contextable with UseRecord {
  import org.goldenport.sdoc.SDoc._

  var title: SDoc = null
  var summary: SDoc = null 

  protected final def init_context(aContext: G3Context) {
    setContext(aContext)
  }

  protected final def push_context(aContext: G3SubContext) {
    pushContext(aContext)
  }

  // Guice
  override def configure() {
  }

  private def add_node[T <: G3Node](node: T):T = {
    context.currentNode += node
    node
  }

  protected final def add_channel_node[T <: G3Channel](atom: Symbol, ch: T):T = {
    add_node(new G3ChannelNode(atom, ch, context))
    ch
  }

  def getContent(path: String): Option[AnyRef] = {
    context.getContent(path)
  }

  // 
  def realm(realmName: String)(configure: => Unit) {
    configure
  }

  def auth(uri: String): Option[G3Authentication] = {
    context.getAuthentication(uri)
  }

  /**
   * entry channels
   */
  lazy val start = context.createStartChannel()

  def port(path: String, description: Description = null): G3Channel = {
    add_channel_node(Symbol(path), context.createPortChannel(path, Option(description)))
  }

  def timer(rule: String): G3Channel = {
    add_channel_node('TIMER, context.createTimerChannel(rule))
  }

  def watch(rule: String): G3Channel = {
    context.createWatchChannel(rule) // TODO
  }

  /**
   * Generic Channel
   */
  def channel(atom: Symbol): PlainChannel = {
    add_channel_node(atom, context.createPlainChannel())
  }

  /**
   * Subscribe Channel
   */
  def subscribe(atom: Symbol, subscribers: String*): SubscribeChannel = {
    subscribe(atom, subscribers.toList)
  }

  def subscribe(atom: Symbol, subscribers: List[String]): SubscribeChannel = {
    add_channel_node(atom, context.createSubscribeChannel(subscribers))
  }

  /**
   * Agent Channel
   */
  // XXX interval, name parameter, default parameter
  def agent(atom: Symbol)(activity: PartialFunction[AnyRef, AnyRef]): AgentChannel = {
    add_channel_node(atom, context.createAgentChannel(activity))
  }

  /**
   * agent with context
   */
  def agentc(atom: Symbol)(activity: (G3AgentContext) => PartialFunction[AnyRef, AnyRef]): Agent2Channel = {
    add_channel_node(atom, context.createAgent2Channel(activity))
  }

  /**
   * agent with value
   */
  def agentv(atom: Symbol)(activity: PartialFunction[Any, Any]): AgentChannel = {
    val act = activity andThen {
      case v: Boolean => v.asInstanceOf[AnyRef]
      case v: Char => v.asInstanceOf[AnyRef]
      case v: Short => v.asInstanceOf[AnyRef]
      case v: Int => v.asInstanceOf[AnyRef]
      case v: Long => v.asInstanceOf[AnyRef]
      case v: Float => v.asInstanceOf[AnyRef]
      case v: Double => v.asInstanceOf[AnyRef]
      case r: AnyRef => r
    }
    add_channel_node(atom, context.createAgentChannel(act))
  }

  /**
   * agent for start
   */
  def agents(atom: Symbol)(activity: => AnyRef): AgentChannel = {
    add_channel_node(atom, context.createAgentChannel {
      case _ => activity
    })
  }

  /**
   * Service Channel
   */
  def service(atom: Symbol, uri: String): ServiceChannel = {
    add_channel_node(atom, context.createServiceChannel(uri))
  }

  /**
   * Component Channel
   */
  def component(atom: Symbol, className: String, methodName: Option[String]): BeanChannel = {
    add_channel_node(atom, context.createBeanChannel(className, methodName))
  }

  def component(atom: Symbol, className: String): BeanChannel = {
    add_channel_node(atom, context.createBeanChannel(className, None))
  }

  def component(atom: Symbol, comp: G3Component): ComponentChannel = {
    comp.setContext(context)
    add_channel_node(atom, context.createComponentChannel(comp))
  }

  /**
   * DataStoreChannel
   */
/*
  def datastore(atom: Symbol): G3Channel = {
    datastore(atom, new DataStoreAdapter(context.createDataStoreDriver()))
  }
*/

  def datastore(atom: Symbol,
                recordClasses: RecordClassSpace = EmptyRecordClassSpace,
                uri: String = "",
                username: String = "", password: String = "",
                driver: String = ""): G3Channel = {
    datastore(atom, new DataStoreAdapter(recordClasses,
                                         uri, 
                                         username, password,
                                         driver))
  }

  def datastore(atom: Symbol, adapter: DataStoreAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * FileChannel
   */
  def file(atom: Symbol, uri: String = "."): G3Channel = {
    file(atom, new FileAdapter(uri))
  }

  def file(atom: Symbol, adapter: FileAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * FileStoreChannel
   */
  def filestore(atom: Symbol, uri: String): G3Channel = {
    filestore(atom, new FileStoreAdapter(uri))
  }

  def filestore(atom: Symbol, adapter: FileStoreAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * ResourceChannel
   */
  def resource(atom: Symbol, uri: String): G3Channel = {
    resource(atom, new ResourceAdapter(uri))
  }

  def resource(atom: Symbol, adapter: ResourceAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * SqlChannel
   */
  def sqlspace(atom: Symbol, uri: String = "", username: String = "", password: String = "", driver: String = ""): G3Channel = {
    add_channel_node(atom, context.createChannel(new SqlAdapter(uri, username, password, driver)))
  }

  def sqlspace(atom: Symbol, adapter: SqlAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * EntityChannel
   */
  def entityspace(atom: Symbol, driver: G3EntityDriver): G3Channel = {
    add_channel_node(atom, context.createEntityChannel(driver))
  }

  def entityspace(atom: Symbol, adapter: EntityAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * ViewChannel
   */
  def viewspace(atom: Symbol, driver: G3ViewDriver): G3Channel = {
    add_channel_node(atom, context.createViewChannel(driver))
  }

  def viewspace(atom: Symbol, adapter: ViewAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * EventChannel
   */
  def eventspace(atom: Symbol, driver: G3EventDriver): G3Channel = {
    add_channel_node(atom, context.createEventChannel(driver))
  }

  def eventspace(atom: Symbol, adapter: EventAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * HTML
   */
  def html(atom: Symbol, title: String = "",
                         schema: RecordSchema = null)(body: => Node): G3Channel = {
    add_channel_node(atom, context.createHtmlChannel(
      new Html(body, title = title, schema = Option(schema))))
  }

  /**
   * WebSocket
   */
  def websocket(atom: Symbol, path: String): G3Channel = {
    add_channel_node(atom, context.createWebSocketChannel(path))
  }

  def websocket(atom: Symbol, adapter: WebSocketAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * UiChannel
   */
  @deprecated("unused")
  def ui(atom: Symbol, adapter: UiAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * ServletChannel
   */
  def servlet(atom: Symbol, servlet: HttpServlet): G3Channel = {
    _servlet(atom, ServletAdapter.create(servlet))
  }

  def servlet(atom: Symbol, servlet: Class[HttpServlet]): G3Channel = {
    _servlet(atom, ServletAdapter.create(servlet))
  }

  def servlet(atom: Symbol, servlet: String): G3Channel = {
    _servlet(atom, ServletAdapter.create(servlet, context))
  }

  def servlet(atom: Symbol, adapter: ServletAdapter): G3Channel = {
    _servlet(atom, adapter)
  }

  private def _servlet(atom: Symbol, adapter: ServletAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * JaxRsChannel
   */
  def jaxrs(atom: Symbol, bean: AnyRef): G3Channel = {
    _jaxrs(atom, JaxRsAdapter.create(bean))
  }

  def jaxrs(atom: Symbol, klass: Class[_]): G3Channel = {
    _jaxrs(atom, JaxRsAdapter.create(klass))
  }

  def jaxrs(atom: Symbol, className: String): G3Channel = {
    _jaxrs(atom, JaxRsAdapter.create(className, context))
  }

  def jaxrs(atom: Symbol, adapter: JaxRsAdapter): G3Channel = {
    _jaxrs(atom, adapter)
  }

  private def _jaxrs(atom: Symbol, adapter: JaxRsAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }

  /**
   * Goldenport
   */
  def goldenport(atom: Symbol, desc: GApplicationDescriptor): G3Channel = {
    _goldenport(atom, GoldenportAdapter.create(desc))
  }

  def goldenport(atom: Symbol, gp: Goldenport): G3Channel = {
    _goldenport(atom, GoldenportAdapter.create(gp))
  }

  private def _goldenport(atom: Symbol, adapter: GoldenportAdapter): G3Channel = {
    add_channel_node(atom, context.createChannel(adapter))
  }
}
